package main

import (
	"fmt"
	//"builtin"
	//"log"
	//"os"
	"path"
	"runtime"
	"strings"
	"time"
	//"io"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	//"sync"
	//"net/url"
	"regexp"
	//"bufio"
	"bytes"
)

/***************************************************************************************************
    Global Variables
***************************************************************************************************/
var gCurCookies []*http.Cookie
var gCurCookieJar *cookiejar.Jar

/***************************************************************************************************
    Functions
***************************************************************************************************/
//do init before all others
func initAll() {
	gCurCookies = nil
	//var err error;
	gCurCookieJar, _ = cookiejar.New(nil)

	initCrifanLib()
}

//do some init for crifanLib
func initCrifanLib() {
	fmt.Println("init for crifanLib")
	gCurCookies = nil
	return
}

// GetCurFilename
// Get current file name, without suffix
func GetCurFilename() string {
	_, fulleFilename, _, _ := runtime.Caller(0)
	//fmt.Println(fulleFilename)
	var filenameWithSuffix string
	filenameWithSuffix = path.Base(fulleFilename)
	//fmt.Println("filenameWithSuffix=", filenameWithSuffix)
	var fileSuffix string
	fileSuffix = path.Ext(filenameWithSuffix)
	//fmt.Println("fileSuffix=", fileSuffix)

	var filenameOnly string
	filenameOnly = strings.TrimSuffix(filenameWithSuffix, fileSuffix)
	//fmt.Println("filenameOnly=", filenameOnly)

	return filenameOnly
}

//get url response html
func getUrlRespHtml(strUrl string, postDict map[string]string) string {
	fmt.Println("in getUrlRespHtml, strUrl=%s", strUrl)
	fmt.Println("postDict=%s", postDict)

	var respHtml string = ""

	httpClient := &http.Client{
		//Transport:nil,
		//CheckRedirect: nil,
		Jar: gCurCookieJar,
	}

	var httpReq *http.Request
	//var newReqErr error
	if nil == postDict {
		fmt.Println("is GET")
		//httpReq, newReqErr = http.NewRequest("GET", strUrl, nil)
		httpReq, _ = http.NewRequest("GET", strUrl, nil)
		// ...
		//httpReq.Header.Add("If-None-Match", `W/"wyzzy"`)
	} else {
		//【记录】go语言中实现http的POST且传递对应的post data
		//http://www.crifan.com/go_language_http_do_post_pass_post_data
		fmt.Println("is POST")
		postValues := url.Values{}
		for postKey, PostValue := range postDict {
			postValues.Set(postKey, PostValue)
		}
		fmt.Println("postValues=%s", postValues)
		postDataStr := postValues.Encode()
		fmt.Println("postDataStr=%s", postDataStr)
		postDataBytes := []byte(postDataStr)
		fmt.Println("postDataBytes=%s", postDataBytes)
		postBytesReader := bytes.NewReader(postDataBytes)
		//httpReq, newReqErr = http.NewRequest("POST", strUrl, postBytesReader)
		httpReq, _ = http.NewRequest("POST", strUrl, postBytesReader)
		//httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
		httpReq.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	}

	httpResp, err := httpClient.Do(httpReq)
	// ...

	//httpResp, err := http.Get(strUrl)
	//fmt.Println("http.Get done")
	if err != nil {
		fmt.Println("http get strUrl=%s response error=%s\n", strUrl, err.Error())
	}
	fmt.Println("httpResp.Header=%s", httpResp.Header)
	fmt.Println("httpResp.Status=%s", httpResp.Status)

	defer httpResp.Body.Close()
	// fmt.Println("defer httpResp.Body.Close done")

	body, errReadAll := ioutil.ReadAll(httpResp.Body)
	//fmt.Println("ioutil.ReadAll done")
	if errReadAll != nil {
		fmt.Println("get response for strUrl=%s got error=%s\n", strUrl, errReadAll.Error())
	}
	//fmt.Println("body=%s\n", body)

	gCurCookies = gCurCookieJar.Cookies(httpReq.URL)
	//fmt.Println("httpResp.Cookies done")

	//respHtml = "just for test log ok or not"
	respHtml = string(body)
	//fmt.Println("httpResp body []byte to string done")

	return respHtml
}

func dbgPrintCurCookies() {
	var cookieNum int = len(gCurCookies)
	fmt.Println("cookieNum=%d", cookieNum)
	for i := 0; i < cookieNum; i++ {
		var curCk *http.Cookie = gCurCookies[i]
		//fmt.Println("curCk.Raw=%s", curCk.Raw)
		fmt.Println("------ Cookie [%d]------", i)
		fmt.Println("Name\t\t=%s", curCk.Name)
		fmt.Println("Value\t=%s", curCk.Value)
		fmt.Println("Path\t\t=%s", curCk.Path)
		fmt.Println("Domain\t=%s", curCk.Domain)
		fmt.Println("Expires\t=%s", curCk.Expires)
		fmt.Println("RawExpires\t=%s", curCk.RawExpires)
		fmt.Println("MaxAge\t=%d", curCk.MaxAge)
		fmt.Println("Secure\t=%t", curCk.Secure)
		fmt.Println("HttpOnly\t=%t", curCk.HttpOnly)
		fmt.Println("Raw\t\t=%s", curCk.Raw)
		fmt.Println("Unparsed\t=%s", curCk.Unparsed)
	}
}

func main1() {
	initAll()

	//step1: access baidu url to get cookie BAIDUID
	fmt.Println("====== 步骤1：获得Cookie ======")
	var url string = "http://www.jd.com/"
	respHtml := getUrlRespHtml(url, nil)
	fmt.Println("respHtml=%s", respHtml)
	dbgPrintCurCookies()

	//check cookie
	var bGotCookieBaiduid = false
	toCheckCookieNameList := []string{"BAIDUID"}
	toCheckCookieNum := len(toCheckCookieNameList)
	fmt.Println("toCheckCookieNum=%d", toCheckCookieNum)
	curCookieNum := len(gCurCookies)
	fmt.Println("curCookieNum=%d", curCookieNum)
	for i := 0; i < toCheckCookieNum; i++ {
		toCheckCkName := toCheckCookieNameList[i]
		fmt.Println("[%d]toCheckCkName=%s", i, toCheckCkName)
		for j := 0; j < curCookieNum; j++ {
			curCookie := gCurCookies[j]
			if strings.EqualFold(toCheckCkName, curCookie.Name) {
				bGotCookieBaiduid = true
				break
			}
		}
	}

	if bGotCookieBaiduid {
		fmt.Println("Found cookie BAIDUID")
	} else {
		fmt.Println("Not found cookie BAIDUID")
	}

	//step2: login, pass paras, extract resp cookie
	fmt.Println("====== 步骤2：提取login_token ======")
	bExtractTokenValueOK := false
	strLoginToken := ""
	var getApiRespHtml string
	if bGotCookieBaiduid {
		var getapiUrl string = "https://passport.jd.com/new/login.aspx"
		getApiRespHtml = getUrlRespHtml(getapiUrl, nil)
		fmt.Println("getApiRespHtml=%s", getApiRespHtml)
		dbgPrintCurCookies()

		//bdPass.api.params.login_token='278623fc5463aa25b0189ddd34165592';
		//use regex to extract login_token
		//【记录】go语言中用正则表达式查找某个值
		//http://www.crifan.com/go_language_regular_expression_find_value/
		loginTokenP, _ := regexp.Compile(`bdPass\.api\.params\.login_token='(?P<loginToken>\w+)';`)
		//loginToken := loginTokenP.FindString(getApiRespHtml);
		//loginToken := loginTokenP.FindSubmatch(getApiRespHtml);
		foundLoginToken := loginTokenP.FindStringSubmatch(getApiRespHtml)
		fmt.Println("foundLoginToken=%s", foundLoginToken)
		if nil != foundLoginToken {
			strLoginToken = foundLoginToken[1] //tmp go regexp not support named group, so use index here
			fmt.Println("found bdPass.api.params.login_token=%s", strLoginToken)
			bExtractTokenValueOK = true
		} else {
			fmt.Println(" not found login_token from html=%s", getApiRespHtml)
		}
	}

	//step3: verify returned cookies
	bLoginBaiduOk := false
	if bGotCookieBaiduid && bExtractTokenValueOK {
		fmt.Println("======步骤3：登陆百度并检验返回的Cookie ======")
		staticPageUrl := "https://passport.jd.com/new/login.aspx"

		postDict := map[string]string{}
		//postDict["ppui_logintime"] = ""
		postDict["charset"] = "utf-8"
		//postDict["codestring"] = ""
		postDict["token"] = strLoginToken
		postDict["isPhone"] = "false"
		postDict["index"] = "0"
		//postDict["u"] = ""
		//postDict["safeflg"] = "0"
		postDict["staticpage"] = staticPageUrl
		postDict["loginType"] = "1"
		postDict["tpl"] = "mn"
		postDict["callback"] = "parent.bdPass.api.login._postCallback"

		//【已解决】go语言中获得控制台输入的字符串
		//http://www.crifan.com/go_language_get_console_input_string/
		strBaiduUsername := "snxamdf"
		strBaiduPassword := "snxamdfsxm0"

		postDict["loginname"] = strBaiduUsername
		postDict["loginpwd"] = strBaiduPassword
		postDict["nloginpwd"] = strBaiduPassword
		postDict["PZSAMYDVIK"] = "bCcqS"
		postDict["chkRememberMe"] = "on"
		postDict["uuid"] = "30ef1e45-1bac-4b2a-b81b-4dce499dff4e"

		fmt.Println("postDict=%s", postDict)

		baiduMainLoginUrl := "https://passport.jd.com/uc/loginService?uuid=30ef1e45-1bac-4b2a-b81b-4dce499dff4e&&r=0.47298651398159564&version=2015"
		loginBaiduRespHtml := getUrlRespHtml(baiduMainLoginUrl, postDict)
		fmt.Println("loginBaiduRespHtml=%s", loginBaiduRespHtml)
		dbgPrintCurCookies()

		//check resp cookies exist or not
		cookieNameDict := map[string]bool{
			"BDUSS":  false,
			"PTOKEN": false,
			"STOKEN": false,
			//"SAVEUSERID": false, //be deleted
		}

		for cookieName, _ := range cookieNameDict {
			for _, singleCookie := range gCurCookies {
				//if(strings.EqualFold(cookieName, singleCookie.Name)){
				if cookieName == singleCookie.Name {
					cookieNameDict[cookieName] = true
					fmt.Println("Found cookie %s", cookieName)
				}
			}
		}
		fmt.Println("After check resp cookie, cookieNameDict=%s", cookieNameDict)

		bAllCookiesFound := true
		for _, bIsExist := range cookieNameDict {
			bAllCookiesFound = bAllCookiesFound && bIsExist
		}
		bLoginBaiduOk = bAllCookiesFound
		if bLoginBaiduOk {
			fmt.Println("成功模拟登陆百度首页！")
		} else {
			fmt.Println("模拟登陆百度首页 失败！")
			fmt.Println("所返回的HTML源码为：" + loginBaiduRespHtml)
		}
	}

	//【workaround】go语言中用log4go输出信息时有bug：只输出部分信息，甚至是无任何输出
	//http://www.crifan.com/go_language_log4go_only_output_part_info/
	time.Sleep(100 * time.Millisecond)
}
